package deployment

import (
	"context"
	"fmt"
	"github.com/sirupsen/logrus"
	v01 "k8s.io/api/apps/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/json"
	"k8s.io/client-go/kubernetes"
	"reflect"
	"time"
)

func CreateDeployment(clientset *kubernetes.Clientset, deployment *v01.Deployment, namespace string, log *logrus.Logger) (*v01.Deployment, error) {
	log.Infof("Creating Deployment...")
	// Deployment 来自于 App
	createdDeployment, err := clientset.AppsV1().Deployments(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
	if err != nil {
		log.Errorf("Failed to create Deployment: %v", err)
		return nil, err
	}
	log.Infof("Created Deployment: %s", createdDeployment.Name)
	return createdDeployment, nil
}

func DeleteDeployment(clientset *kubernetes.Clientset, namespace, name string, log *logrus.Logger) error {
	log.Infof("Deleting Deployment...")
	//
	err := clientset.AppsV1().Deployments(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
	if err != nil {
		log.Errorf("Failed to delete Deployment: %v", err)
		return err
	}
	log.Info("Deployment deleted")
	return nil
}

func PatchDeployment(clientset *kubernetes.Clientset, deploymentName, namespace string, patchData map[string]interface{}, log *logrus.Logger) (*v01.Deployment, error) {
	pathBytes, err := json.Marshal(patchData) // 转换为了所需参数：字节数组
	if err != nil {
		return nil, fmt.Errorf("failed to marshal patch data: %w", err)
	}
	//log.Debugf()

	updatedDeployment, err := clientset.AppsV1().Deployments(namespace).Patch(
		context.TODO(),
		deploymentName,
		types.StrategicMergePatchType, // 使用默认的策略合并更新
		pathBytes,
		metav1.PatchOptions{},
	)
	if err != nil {
		return nil, fmt.Errorf("failed to path deployment %s/%s: %w", deploymentName, namespace, err)
	}

	log.Info("Delployment %s/%s updated successfully using StrategicMergePatchType", deploymentName, namespace)

	return updatedDeployment, nil
}

// 这些是 Conditions: type,status(TF),reason
// Deployment 状态为 Progressing,Complete,(但是其仍显示Progressing,Reason是avaible)
// status:False,reason:ProgressDeadlineExceeded

func CheckDeploymentStatus(clientset *kubernetes.Clientset, deploymentName, namespace string, timeout time.Duration, log *logrus.Logger) error {
	ctx, cancle := context.WithTimeout(context.Background(), timeout)
	defer cancle()
	ticker := time.NewTicker(2 * time.Second)
	defer ticker.Stop()

	expectedConditions := []v01.DeploymentCondition{
		{
			Type:   "Available",
			Status: "True",
			Reason: "MinimumReplicasAvailable",
		},
		{
			Type:   "Progressing",
			Status: "True",
			Reason: "NewReplicaSetAvailable",
		},
	}

	for {
		select {
		case <-ctx.Done():
			return fmt.Errorf("timeout waiting for Deployment %s to ?: %w", deploymentName, ctx.Err())
		case <-ticker.C:
			deployment, err := clientset.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{})
			log.Infof("Deployment name is %s", deployment.Name)
			// ?
			if err != nil {
				log.Warnf("Error getting Deployment %s", deploymentName)
				continue
			}
			// 试用反射中的 DeepEqual来比较结构体的对应字段是否相等
			if reflect.DeepEqual(deployment.Status.Conditions, expectedConditions) {
				return nil
			}
			//
			// 这里可以根据 Conditions 切片的长度进行判断吗？
			log.Debugf("Deployment %s current")
		}
	}
}
